package ghttp

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
)

// 请求封装
type Request struct {
	RawReq  *http.Request      // 原始请求
	Config  *HttpConfig        // 请求级配置
	isMulti bool               // 是否为并发请求
	doOnly  bool               // 请求完成不读Body
	Form  *url.Values // form传参
}

// 应答封装
type Response struct {
	RawResp *http.Response // 原始应答
	Body    []byte         // 应答体
	Err     error          // 错误
}

// 创建Do请求
func NewDoRequest(rawReq *http.Request, config *HttpConfig) (req *Request, err error) {
	req = &Request{
		RawReq: rawReq,
		Config: config,
		doOnly: true,
	}
	return
}

// 创建GET请求
func NewGetRequest(reqUrl string, query *url.Values, config *HttpConfig) (req *Request, err error) {
	// 构造请求
	var rawReq *http.Request
	if rawReq, err = http.NewRequest("GET", reqUrl, nil); err != nil {
		return
	}
	if query != nil {
		rawReq.URL.RawQuery = query.Encode()
	}
	req = &Request{
		RawReq: rawReq,
		Config: config,
	}
	return
}

// 创建POST表单请求
func NewPostRequest(reqUrl string, query *url.Values, form *url.Values, config *HttpConfig) (req *Request, err error) {
	// POST表单
	var formData io.Reader = nil
	if form != nil {
		formData = bytes.NewBufferString(form.Encode())
	}

	// 构造请求
	var rawReq *http.Request
	if rawReq, err = http.NewRequest("POST", reqUrl, formData); err != nil {
		return
	}
	if query != nil {
		rawReq.URL.RawQuery = query.Encode()
	}
	rawReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	req = &Request{
		RawReq: rawReq,
		Config: config,
		Form: form,
	}
	return
}

// 创建POST JSON请求
func NewPostJsonRequest(reqUrl string, query *url.Values, json []byte, config *HttpConfig) (req *Request, err error) {
	// 构造请求
	var rawReq *http.Request
	if rawReq, err = http.NewRequest("POST", reqUrl, bytes.NewBuffer(json)); err != nil {
		return
	}
	if query != nil {
		rawReq.URL.RawQuery = query.Encode()
	}
	rawReq.Header.Add("Content-Type", "application/json")
	req = &Request{
		RawReq: rawReq,
		Config: config,
	}
	return
}

// 创建POST FILE请求
func NewPostFileRequest(reqUrl string, query *url.Values, form *url.Values, files map[string]io.Reader, config *HttpConfig) (req *Request, err error) {
	var rawReq *http.Request // 构造请求

	formData := &bytes.Buffer{}
	writer := multipart.NewWriter(formData)

OuterLoop:
	for {
		// form 表单字段
		if form != nil {
			for formKey := range *form {
				if err = writer.WriteField(formKey, form.Get(formKey)); err != nil {
					break OuterLoop
				}
			}
		}
		// form 文件
		if files != nil {
			for filesKey, filesValue := range files {
				var part io.Writer
				var fileName string

				if file, ok := filesValue.(*os.File); ok {
					fileName = file.Name()
				} else {
					fileName = filesKey
				}

				if part, err = writer.CreateFormFile(filesKey, fileName); err != nil {
					break OuterLoop
				}
				if _, err = io.Copy(part, filesValue); err != nil {
					break OuterLoop
				}
			}
		}
		break
	}
	if err1 := writer.Close(); err1 != nil {
		err = err1
		return
	}
	if err != nil {
		return
	}

	if rawReq, err = http.NewRequest("POST", reqUrl, formData); err != nil {
		return
	}

	if query != nil {
		rawReq.URL.RawQuery = query.Encode()
	}
	rawReq.Header.Set("Content-Type", writer.FormDataContentType())

	req = &Request{
		RawReq: rawReq,
		Config: config,
	}
	return
}
